#include"lars_api.h"
#include"lars.pb.h"
#include"lars_reactor.h"

// 构造函数
lars_client::lars_client(){
    // 0.初始化消息序号
    _seqid = 0;

    // 1.初始化服务器地址
    struct sockaddr_in servaddr;
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    //默认的ip地址是本地，因为是API和agent应该部署于同一台主机上
    inet_aton("127.0.0.1", &servaddr.sin_addr);

    // 2.创建3个UDP client的文件描述符
    for(int i=0; i<3; i++){
        // 创建文件描述符
        _sockfd[i] = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, IPPROTO_UDP);
        if(_sockfd[i] == -1){
            fprintf(stderr, "create socket fd:%d error\n", _sockfd[i]);
            exit(1);
        }

        // 设置服务器端口, 分别是8888, 88889, 8890
        servaddr.sin_port = htons(8888+i);

        // 连接
        int ret = connect(_sockfd[i], (const struct sockaddr*)&servaddr, sizeof(servaddr));
        if(ret == -1){
            fprintf(stderr, "fd:%d connect error\n", _sockfd[i]);
            exit(1);
        }

        // 连接成功
        // printf("udp server connection success fd:%d, port:%d!\n", _sockfd[i], 8888+i);

    }

}

// 在使用一个mod之前，提前注册当前的情况
int lars_client::reg_init(int modid, int cmdid){
    route_set route;
    // 重复尝试3次
    int retry_cnt = 3;

    int ret;
    for(int i=0; i<retry_cnt; i++){
        ret = get_route(modid, cmdid, route);

        if(ret != lars::RET_NOEXIST){
            break;
        }

        // 50 ms尝试查询一次
        usleep(50000); 
    }
 
    return ret;
}

int lars_client::get_host(int modid, int cmdid, std::string &ip, int &port){
    // 0.序号加1
    uint32_t seq = _seqid++;

    // 1.封装一个请求host的消息体
    lars::GetHostRequest request;
    request.set_modid(modid);
    request.set_cmdid(cmdid);
    request.set_seq(seq);

    // 2.将消息体打包成能够识别的消息
    
    // 2.1.设置读写缓存
    char write_buf[4096], read_buf[10*4096];

    // 2.2.消息头
    msg_head head;
    head.msgid = lars::ID_GetHostRequest;
    head.msglen = request.ByteSizeLong();
    memcpy(write_buf, &head, MESSAGE_HEAD_LEN);

    // 2.3.消息体
    request.SerializeToArray(write_buf+MESSAGE_HEAD_LEN, head.msglen);

    // 2.4.发送数据
    int index = (modid + cmdid) % 3;
    // printf("get_host:send to agent server\n");
    int ret = sendto(_sockfd[index], write_buf, head.msglen+MESSAGE_HEAD_LEN, 0, NULL, 0);
    if(ret == -1){
        perror("sendto error");
        return lars::RET_SYSTEM_ERROR;
    }

    // 3.阻塞等待接收数据
    int message_len;
    lars::GetHostResponse response;

    // do while是为了防止用户同时发了多个udp查询，但是服务器回复较慢，还没有到想要的seq
    do{
        message_len = recvfrom(_sockfd[index], read_buf, sizeof(read_buf), 0, NULL, 0);
        if(message_len == -1){
            perror("recvfrom error");
            return lars::RET_SYSTEM_ERROR;
        }
        
        // 4.得到消息头
        memcpy(&head, read_buf, MESSAGE_HEAD_LEN);
        if(head.msgid != lars::ID_GetHostResponse){
            fprintf(stderr, "message id is error\n");
            return lars::RET_SYSTEM_ERROR;
        }

        // 5.得到消息体, 解析数据
        ret = response.ParseFromArray(read_buf+MESSAGE_HEAD_LEN, head.msglen);
        if(!ret){
            fprintf(stderr, "reponse.ParseFromArray error: head.msglen = %d, message_len = %d, message_len - MESSAGE_HEAD_LEN = %d, head msgid = %d, ID_GetHostResponse = %d\n", 
                head.msglen, message_len, message_len-MESSAGE_HEAD_LEN, head.msgid, lars::ID_GetHostResponse);
            
            return lars::RET_SYSTEM_ERROR;
        }
    }while(response.seq()<seq);   // 直到服务器返回一个跟当前的seq相同的包
    
    // 如果当前的 seq|modid|cmdid有一项不对，则整个数据还是有问题的
    if(response.seq()!=seq || response.modid()!=modid || response.cmdid()!=cmdid){
        fprintf(stderr, "message format error\n");
        return lars::RET_SYSTEM_ERROR;
    }

    // 6.处理消息
    if(response.retcode() == lars::RET_SUCC){
        lars::HostInfo host;
        host = response.host();

        struct in_addr inadrr;
        inadrr.s_addr = host.ip();

        ip = inet_ntoa(inadrr);
        port = host.port();
    }

    return response.retcode();
}


// api获取当前modid和cmdid下的全部host信息
int lars_client::get_route(int modid, int cmdid, route_set &route){

    // 1.封装一个请求host的消息体
    lars::GetRouteRequest request;
    request.set_modid(modid);
    request.set_cmdid(cmdid);

    // 2.将消息体打包成能够识别的消息
    
    // 2.1.设置读写缓存
    char write_buf[4096], read_buf[10*4096];

    // 2.2.消息头
    msg_head head;
    head.msgid = lars::ID_API_GetRouteRequest;
    head.msglen = request.ByteSizeLong();
    memcpy(write_buf, &head, MESSAGE_HEAD_LEN);

    // 2.3.消息体
    request.SerializeToArray(write_buf+MESSAGE_HEAD_LEN, head.msglen);

    // 2.4.发送数据
    int index = (modid + cmdid) % 3;

    int ret = sendto(_sockfd[index], write_buf, head.msglen+MESSAGE_HEAD_LEN, 0, NULL, 0);
    if(ret == -1){
        perror("sendto error");
        return lars::RET_SYSTEM_ERROR;
    }

    // 3.阻塞等待接收数据
    int message_len;
    lars::GetRouteResponse response;

    message_len = recvfrom(_sockfd[index], read_buf, sizeof(read_buf), 0, NULL, 0);
    if(message_len == -1){
        perror("recvfrom error");
        return lars::RET_SYSTEM_ERROR;
    }
    
    // 4.得到消息头
    memcpy(&head, read_buf, MESSAGE_HEAD_LEN);
    if(head.msgid != lars::ID_API_GetRouteResponse){
        fprintf(stderr, "message id is error\n");
        return lars::RET_SYSTEM_ERROR;
    }

    // 5.得到消息体, 解析数据
    ret = response.ParseFromArray(read_buf+MESSAGE_HEAD_LEN, head.msglen);
    if(!ret){
        fprintf(stderr, "reponse.ParseFromArray error: head.msglen = %d, message_len = %d, message_len - MESSAGE_HEAD_LEN = %d, head msgid = %d, ID_GetHostResponse = %d\n", 
            head.msglen, message_len, message_len-MESSAGE_HEAD_LEN, head.msgid, lars::ID_GetHostResponse);
        
        return lars::RET_SYSTEM_ERROR;
    }
    
    // 如果当前的 seq|modid|cmdid有一项不对，则整个数据还是有问题的
    if(response.modid()!=modid || response.cmdid()!=cmdid){
        fprintf(stderr, "message format error\n");
        return lars::RET_SYSTEM_ERROR;
    }
 
    // 6.处理消息
    lars::HostInfo host;

    struct in_addr inadrr;
    std::string ip;
    int port;

    if(response.host_size()==0){
        return lars::RET_NOEXIST;
    }
    
    for(int i=0; i<response.host_size(); i++){
        // 获取host的IP和port
        host = response.host(i);
        inadrr.s_addr = ntohl(host.ip());
        ip = inet_ntoa(inadrr);
        port = host.port();

        route.push_back({ip, port});
    }

    return lars::RET_SUCC;
}


// api上报给agent host的调用情况
int lars_client::report_host(int modid, int cmdid, std::string ip, int port, int retcode){
    // printf("report_host:send to agent server\n");
    
    lars::ReportRequest request;
    // 组装消息体
    request.set_modid(modid);
    request.set_cmdid(cmdid);
    request.set_retcode(retcode);

    // 组装主机信息
    lars::HostInfo *host = request.mutable_host();
    
    struct in_addr inadrr;
    inet_aton(ip.c_str(), &inadrr);
    host->set_ip(inadrr.s_addr);

    host->set_port(port);


    // 将消息发送出去
    char write_buf[4096];

    // 消息头
    msg_head head;
    head.msgid = lars::ID_ReportRequest;
    head.msglen = request.ByteSizeLong();

    memcpy(write_buf, &head, MESSAGE_HEAD_LEN);

    // 2.3.消息体
    request.SerializeToArray(write_buf+MESSAGE_HEAD_LEN, head.msglen);

    // 2.4.发送数据
    int index = (modid + cmdid) % 3;
    
    int ret = sendto(_sockfd[index], write_buf, head.msglen+MESSAGE_HEAD_LEN, 0, NULL, 0);
    if(ret == -1){
        perror("sendto error");
        return lars::RET_SYSTEM_ERROR;
    }

    return lars::RET_SUCC;

}


lars_client::~lars_client(){
    // 关闭3个文件描述符
    for(int i=0; i<3; i++){
        close(_sockfd[i]);
    }

}